#include "video_device.h"
#include <err.h>
#include <unistd.h>
#include <stdio.h>

video_device::video_device(const char* video_name,QObject *parent):
    devname(video_name),QObject(parent)
{
    bmpheader();
    init_video_device(devname);
    setframerate( FPS);
    fflush(stdout);
}

    int video_device::init_video_device(const char *devname)
    {
          int i;


          //opendev
          if ((fd_video = open(devname, O_RDWR)) == -1)
          {
              printf("Error opening V4L interface\n");
              return (FALSE);
          }

          //query cap
          if (ioctl(fd_video, VIDIOC_QUERYCAP, &cap) == -1)
          {
              printf("Error opening device %s: unable to query device.\n",FILE_VIDEO);
              return (FALSE);
          }
          else
          {
               printf("driver:\t\t%s\n",cap.driver);
               printf("card:\t\t%s\n",cap.card);
               printf("bus_info:\t%s\n",cap.bus_info);
               printf("version:\t%d\n",cap.version);
               printf("capabilities:\t%x\n",cap.capabilities);

               if ((cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) == V4L2_CAP_VIDEO_CAPTURE)
               {
                  printf("Device %s: supports capture.\n",FILE_VIDEO);
              }

              if ((cap.capabilities & V4L2_CAP_STREAMING) == V4L2_CAP_STREAMING)
              {
                  printf("Device %s: supports streaming.\n",FILE_VIDEO);
              }
          }

          //emu all support fmt
          fmtdesc.index=0;
          fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
          printf("Support format:\n");
          while(ioctl(fd_video,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
          {
              printf("\t%d.%s\n",fmtdesc.index+1,fmtdesc.description);
              fmtdesc.index++;
          }

          //set fmt
          fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
          fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
          fmt.fmt.pix.height = IMAGE_HEIGHT;
          fmt.fmt.pix.width = IMAGE_WIDTH;
          fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

          if(ioctl(fd_video, VIDIOC_S_FMT, &fmt) == -1)
          {
              printf("Unable to set format\n");
              return FALSE;
          }
          if(ioctl(fd_video, VIDIOC_G_FMT, &fmt) == -1)
          {
              printf("Unable to get format\n");
              return FALSE;
          }
          {
               printf("fmt.type:\t\t%d\n",fmt.type);
               printf("pix.pixelformat:\t%c%c%c%c\n",fmt.fmt.pix.pixelformat & 0xFF, (fmt.fmt.pix.pixelformat >> 8) & 0xFF,(fmt.fmt.pix.pixelformat >> 16) & 0xFF, (fmt.fmt.pix.pixelformat >> 24) & 0xFF);
               printf("pix.height:\t\t%d\n",fmt.fmt.pix.height);
               printf("pix.width:\t\t%d\n",fmt.fmt.pix.width);
               printf("pix.field:\t\t%d\n",fmt.fmt.pix.field);
          }
          //set fps
//          setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
//          setfps.parm.capture.timeperframe.numerator = 10;
//          setfps.parm.capture.timeperframe.denominator = 10;


          unsigned int n_buffers;

            //request for 4 buffers
            req.count=4;
            req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
            req.memory=V4L2_MEMORY_MMAP;
            if(ioctl(fd_video,VIDIOC_REQBUFS,&req)==-1)
            {
                printf("request for buffers error\n");
            }

            //mmap for buffers
            buffers = (buffer*)malloc(req.count*sizeof (*buffers));
            if (!buffers)
            {
                printf ("Out of memory\n");
                return(FALSE);
            }

            for (n_buffers = 0; n_buffers < req.count; n_buffers++)
            {
                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory = V4L2_MEMORY_MMAP;
                buf.index = n_buffers;
                //query buffers
                if (ioctl (fd_video, VIDIOC_QUERYBUF, &buf) == -1)
                {
                    printf("query buffer error\n");
                    return(FALSE);
                }

                buffers[n_buffers].length = buf.length;
                //map
                buffers[n_buffers].start = mmap(NULL,buf.length,PROT_READ |PROT_WRITE, MAP_SHARED, fd_video, buf.m.offset);
                if (buffers[n_buffers].start == MAP_FAILED)
                {
                    printf("buffer map error\n");
                    return(FALSE);
                }
            }

            //queue
            for (n_buffers = 0; n_buffers < req.count; n_buffers++)
            {
                buf.index = n_buffers;
                ioctl(fd_video, VIDIOC_QBUF, &buf);
            }

            type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            ioctl (fd_video, VIDIOC_STREAMON, &type);

            FD_ZERO(&fds);
            FD_SET(fd_video, &fds);
            tv.tv_sec = 2; /* Timeout. */
            tv.tv_usec = 0;

    }

    int video_device::setframerate(unsigned char rate)
    {

      setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
      setfps.parm.capture.timeperframe.numerator = 1;
      setfps.parm.capture.timeperframe.denominator = rate;
      if(-1 == ioctl(fd_video, VIDIOC_S_PARM, &setfps)) warn("帧率设置失败！");
      if(ioctl(fd_video, VIDIOC_G_PARM, &setfps)>=0)
      {
          printf("numerator: %d, denominator: %d\n",
                 setfps.parm.capture.timeperframe.numerator,
       setfps.parm.capture.timeperframe.denominator);
      }

  }


    int video_device::video_grab(void)
    {
         ioctl(fd_video, VIDIOC_DQBUF, &buf);
         select(fd_video + 1, &fds, NULL, NULL, &tv);
         yuyv_2_rgb888();

       frame=QImage(frame_buffer,IMAGE_WIDTH,IMAGE_HEIGHT,QImage::Format_RGB888);

         ioctl(fd_video, VIDIOC_QBUF, &buf);
    }

    #define BALANCE(X) if(X>255)X = 255;else if(X<0)X = 0;

    int video_device::yuyv_2_rgb888(void)
    {
          int           i,j;
          unsigned char y1,y2,u,v;
          int r1,g1,b1,r2,g2,b2;
          char * pointer;

          pointer =(char*)buffers[0].start;

          for(i=0;i<IMAGE_HEIGHT;i++)
          {
          for(j=0;j<IMAGE_WIDTH/2;j++)//每次取4个字节，也就是两个像素点，转换rgb，6个字节，还是两个像素点
          {
          y1 = *( pointer + (i*IMAGE_WIDTH/2+j)*4);
          u  = *( pointer + (i*IMAGE_WIDTH/2+j)*4 + 1);
          y2 = *( pointer + (i*IMAGE_WIDTH/2+j)*4 + 2);
          v  = *( pointer + (i*IMAGE_WIDTH/2+j)*4 + 3);

          r1 = y1 + 1.042*(v-128);
          g1 = y1 - 0.34414*(u-128) - 0.71414*(v-128);
          b1 = y1 + 1.772*(u-128);

          r2 = y2 + 1.042*(v-128);
          g2 = y2 - 0.34414*(u-128) - 0.71414*(v-128);
          b2 = y2 + 1.772*(u-128);

          BALANCE(r1)
          BALANCE(b1)
          BALANCE(g1)
          BALANCE(r2)
          BALANCE(b2)
          BALANCE(g2)


          *(frame_buffer + (i*IMAGE_WIDTH/2+j)*6    ) = (unsigned char)r1;
          *(frame_buffer + (i*IMAGE_WIDTH/2+j)*6 + 1) = (unsigned char)g1;
          *(frame_buffer + (i*IMAGE_WIDTH/2+j)*6 + 2) = (unsigned char)b1;
          *(frame_buffer + (i*IMAGE_WIDTH/2+j)*6 + 3) = (unsigned char)r2;
          *(frame_buffer + (i*IMAGE_WIDTH/2+j)*6 + 4) = (unsigned char)g2;
          *(frame_buffer + (i*IMAGE_WIDTH/2+j)*6 + 5) = (unsigned char)b2;
          }
          }
        //  printf("change to RGB OK \n");
    }

    int video_device::close_device(void)
    {

    }
    void video_device::saveimage(QImage &image,QString pathname)
    {
     //   QFile picture("pic");
     //   if (!picture.open(QIODevice::ReadOnly | QIODevice::Text))
                return;
      //  picture.writeData((const char*)&bf,sizeof(bf));
      //  picture.writeData(&bi,sizeof(bi));
        //picture.writeData((const char*)image.bits(),sizeof(image));
      //  picture.flush();
        image.save(pathname);

    }

    void video_device::screenshot(void)
    {
        int i,j;
        unsigned char *lpbuffer;
        unsigned char *word;


        fp = fopen(BMP, "wb");
        fwrite(&bf, 14, 1, fp); //向文件中写入图片文件头
        fwrite(&bi, 40, 1, fp); //向文件中写入图片信息头
        //encode_jpeg(frame_buffer,IMAGEWIDTH,IMAGEHEIGHT); //RGB24 to Jpeg
      fseek(fp, 0x36, SEEK_SET);

     lpbuffer = frame_buffer+IMAGE_WIDTH*(IMAGE_HEIGHT-1)*3;

     for(i=0; i<IMAGE_HEIGHT; i++)    //bmp file scan line is arraned by BGR|BGR|BGR|........
     {
      word = lpbuffer;
      for(j=0; j<IMAGE_WIDTH; j++)
      {
        fputc( *(word+2), fp); // B
        fputc( *(word+1), fp); // G
        fputc( *(word+0), fp); // R
        word+=3;
      }
      lpbuffer -= IMAGE_WIDTH*3; // 指针转到上一行的开始
     }

        fclose(fp);
        printf("save image OK\n");
    }


   void video_device::bmpheader()
    {

        //Set BITMAPINFOHEADER 设置bmp图片的信息头格式
           bi.biSize = 40;  //位图信息头大小
           bi.biWidth = IMAGE_WIDTH;  //图像宽度
           bi.biHeight = IMAGE_HEIGHT;  //图像高度
           bi.biPlanes = 1;   //位平面树=1
           bi.biBitCount = 24;  //单位像素的位数
           bi.biCompression = 0;  //图片的压缩属性，bmp不压缩，等于0
           //bi.biSizeImage = WIDTHBYTES(bi.biWidth * bi.biBitCount) * bi.biHeight;
          bi.biSizeImage = IMAGE_WIDTH * IMAGE_HEIGHT * bi.biBitCount;
        //表示bmp图片数据区的大小，当上一个属性biCompression等于0时，这里的值可以省略不填

           bi.biXPelsPerMeter = 0; //水平分辨率
           bi.biYPelsPerMeter = 0; //垂直分辨率
           bi.biClrUsed = 0;   //表示使用了多少哥颜色索引表，一般biBitCount属性小于16才会用到，等于0时表示有2^biBitCount个颜色索引表
           bi.biClrImportant = 0;  //表示有多少个重要的颜色，等于0时表示所有颜色都很重要

           //Set BITMAPFILEHEADER  设置bmp图片的文件头格式
           bf.bfType = 0x4d42;  //2个字节，恒等于0x4d42，ascii字符“BM”
           bf.bfSize = 54 + bi.biSizeImage; //文件大小，以4个字节为单位
           bf.bfReserved1 = 0;  //备用
           bf.bfReserved2 = 0;  //备用
           bf.bfOffBits = 54;   //数据区在文件中的位置偏移量



    }
